﻿#region Header

// Majesty of Omega - The next evolution of macro management space strategy
// 
// Copyright 2007-2009 André Claaßen
// 
// This file is protected by the terms and conditions of the
// Microsoft Community License (Ms-CL 1.1), a copy of which should
// have been distributed along with this software. If not,
// you may find the license information at the following URL:
// 
// http://www.microsoft.com/resources/sharedsource/licensingbasics/communitylicense.mspx

#endregion

#region

using System;
using System.Linq;
using System.Collections.Generic;
using System.Globalization;
using System.IO;
using System.Reflection;
using System.Xml;
using DesignByContract;
using Majesty_of_Omega.Util;

#endregion

namespace Majesty_of_Omega.Config
{
    /// <summary>
    /// The String Manager reads all XML-String Tables in .\Spreadsheets\Strings\en and generates
    /// a giant Dictionary. 
    /// All in game Strings will be transfered to this tables
    /// </summary>
    public static class StringManager
    {
        /// <summary>
        /// Root-Directory of all embedded string resources
        /// </summary>
        const string stringResourceDir = "Majesty_of_Omega.Spreadsheets.Strings.";

        /// <summary>
        /// Random  number generator for the unicode variant strings. 
        /// See the comments in the <see cref="GetString"/> function
        /// </summary>
        static Random _random = new Random(3343);

        /// <summary>
        /// Sets the random to a new Seed for testing purposes
        /// </summary>
        /// <value>The random.</value>
        public static int RandomSeed
        {
            set { _random = new Random(value); }
        }

        private static Dictionary<string, string> _stringTable;

        /// <summary>
        /// Access to the string Table
        /// </summary>
        /// <remarks>
        /// This function checks, if  a language specific variant of a string table exists. 
        /// If no language specific string table is found, the function switch back to english (en).
        /// The language directories must conform to the Two-Letter ISO Language table.
        /// </remarks>
        private static Dictionary<string, string> StringTable
        {
            get
            {
                if (_stringTable != null) return _stringTable;

                string stringResources = GetResourceDirForCurrentCulture();

                _stringTable = ReadStringTables(stringResources);

                return _stringTable;
            }
        }

        /// <summary>
        /// Gets the resource prefix string for the current culture.
        /// </summary>
        /// <returns>
        /// If no culture dependend string tables found, english is default
        /// </returns>
        static private string GetResourceDirForCurrentCulture()
        {
            Assembly assembly = Assembly.GetExecutingAssembly();
            IEnumerable<string> resourceNames = assembly.GetManifestResourceNames();

            string twoLetterIsoLangName = CultureInfo.CurrentCulture.TwoLetterISOLanguageName;

            if (resourceNames.Any(
                resourceName =>
                resourceName.Contains(stringResourceDir + twoLetterIsoLangName)))
            {
            }
            else
            {
                twoLetterIsoLangName = "en";
            }
            return stringResourceDir + twoLetterIsoLangName;
        }

        /// <summary>
        /// Gets the string for the unicode value. 
        /// </summary>
        /// <remarks>
        /// This function supports unicode variants, to diverse the strings a bit. 
        /// An example is the unicode value in the situation reports, which are separated by 
        /// semicolons.
        /// <example>
        /// "sitrepturn;sitrepturn1;sitrepturn2;sitrepturn3"
        /// </example>
        /// Variants will be selected randomly by this funciton
        /// </remarks>
        /// <param name="unicode">The unicode.</param>
        /// <returns>the string</returns>
        public static string GetString(string unicode)
        {
            try
            {
                string[] unicodeVariants = unicode.Split(';');
                string key = unicodeVariants[_random.Next(unicodeVariants.Length)].ToLower();
                return StringTable[key];
            }
            catch (Exception e)
            {
                throw new SpreadsheetException(SpreadSheetType.StringTables,
                                               "Could not resolve unicode value " + unicode, e);
            }
        }

        /// <summary>
        /// Reads all the string tables from the specified dir
        /// </summary>
        /// <param name="stringTableéDir">The string tableé dir.</param>
        /// <returns></returns>
        private static Dictionary<string, string> ReadStringTables(string stringTableéDir)
        {
            Dictionary<string, string> dictionary = new Dictionary<string, string>();

            Assembly assembly = Assembly.GetExecutingAssembly();
            IEnumerable<string> resourceNames = assembly.GetManifestResourceNames();
            foreach (var resourceName in resourceNames)
            {
                if (!resourceName.Contains(stringTableéDir)) continue;
                using (Stream resourceStream = assembly.GetManifestResourceStream(resourceName))
                {
                    ReadStringTable(resourceStream, dictionary);
                }
            }
            return dictionary;
        }

        /// <summary>
        /// Reads a string table from a stream
        /// </summary>
        /// <param name="resourceStream">The text reader.</param>
        /// <param name="dictionary">The dictionary.</param>
        private static void ReadStringTable(Stream resourceStream, Dictionary<string, string> dictionary)
        {
            XmlDocument doc = new XmlDocument();
            doc.Load(resourceStream);
            Require.IsNotNull(doc.DocumentElement, "String tables must have one document element");
            foreach (var stringNode in doc.DocumentElement.ChildNodes)
            {
                XmlElement element = (XmlElement)stringNode;
                string token = element.Attributes["Tok"].Value.ToLower();
                string value = element.InnerText;
                if (!dictionary.ContainsKey(token))
                    dictionary.Add(token, value);
            }
        }

        /// <summary>
        /// Reads the string table form the string dictionary and replace all
        /// placeholders with the given values from the argument list.
        /// </summary>
        /// <param name="unicode">The unicode identifier from the xml files</param>
        /// <param name="args">
        /// variable number of arguments pairs, which are depends from the type of the event.
        /// An argument consists of a variable identifier string and the value as object.
        /// <example>
        /// reportTurn.AddSitRepEntry(SitReportType.TurnCompleted, "value1", _saveGame.TurnNumber, "value2", "a suprising event text");
        /// The placeholder variables are defined in the SitRepStrings.xml
        /// </example>
        /// </param>
        public static string GetStringFormatted(string unicode, params object[] args)
        {
            string unicodeString = GetString(unicode);
            return StringHelper.ReplacePlaceholders(unicodeString, args);
        }
    }
}